import dgram from 'dgram'
import { WebSocketServer } from 'ws'
import { EventEmitter } from 'events'

export default class Transpoter extends EventEmitter {
  private udp = dgram.createSocket('udp4')
  private options: {
    name: string,
    type: 'TCP' | 'UDP',
    port: number,
    datas: {
      offset: number,
      name: string,
      type: 'int8' | 'uint8' | 'int16' | 'uint16' | 'int32' | 'uint32' | 'int64' | 'uint64' | 'float32' | 'float64',
      rindex: number,
      rtype: 'int8' | 'uint8' | 'int16' | 'uint16' | 'int32' | 'uint32' | 'int64' | 'uint64' | 'float32' | 'float64',
      fn: string,
      fnInst: null | ((v: number, dv: DataView) => number)
    }[]
  }
  constructor(options_: any) {
    super()
    this.options = { ...options_ }
    this.options.datas = [ ...options_.datas ]

    const testDv = new DataView(new ArrayBuffer(640))
    for(let item of this.options.datas) {
      if (!item.fn) {
        continue
      }
      item.fnInst = this.genFn(item.fn)
      try {
        if (typeof item.fnInst?.(0, testDv) !== 'number') {
          item.fnInst = null
        }
      } catch {
        item.fnInst = null
      }
    }
    this.udp.on('message', data => {
      this.transport(data)
    })
    this.udp.on('error', console.log)

    this.udp.bind(this.options.port, '127.0.0.1', () => console.log(1))
  }

  destroy() {
    this.udp.close()
  }

  private transport(data: Buffer) {
    const dvd = new DataView(data.buffer)
    const dv = new DataView(new ArrayBuffer(84))
    for(let item of this.options.datas) {
      let v = dvd[this.getReadFunction(item.rtype)](item.rindex, item.rtype.indexOf('8') > -1 ? undefined : true)
      if (item.fnInst) {
        v = item.fnInst(v, dvd)
      }
      dv[this.setWriteFunction(item.type)](item.offset, v, item.type.indexOf('8') > -1 ? undefined : true)
    }
    this.emit('data', dv.buffer)
  }

  private getReadFunction(type: 'int8' | 'uint8' | 'int16' | 'uint16' | 'int32' | 'uint32' | 'int64' | 'uint64' | 'float32' | 'float64') {
    return `get${type[0].toUpperCase()}${type.slice(1)}`
  }

  private setWriteFunction(type: 'int8' | 'uint8' | 'int16' | 'uint16' | 'int32' | 'uint32' | 'int64' | 'uint64' | 'float32' | 'float64') {
    return `set${type[0].toUpperCase()}${type.slice(1)}`
  }

  private genFn(fn: string) {
    const wordList = fn.split(' ')
    for(let word of wordList) {
      if (word.length < 2) {
        continue
      }
      if (/import|require|process|global|globalThis|this/.test(word.trim())) {
        return null
      }
    }
    return new Function('v', 'dv', `
    ${fn};
    return v;
    `) as (v: number,  dv: DataView) => number
  }

  private exec(v: number, fn: string) {

  }
}